package com.kgar.service.impl;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.io.FileUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.kgar.dao.JobInfoDao;
import com.kgar.dto.*;
import com.kgar.entity.graph.node.JobPositionNode;
import com.kgar.entity.graph.node.ResumeNode;
import com.kgar.entity.graph.node.UserNode;
import com.kgar.entity.graph.relationship.MatchWithRelation;
import com.kgar.entity.pojo.JobInfo;
import com.kgar.repository.JobPositionRepository;
import com.kgar.repository.UserRepository;
import com.kgar.service.JobInfoService;
import com.kgar.util.UserHolder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;

import static com.kgar.util.RedisConstants.*;
import static com.kgar.util.RedisConstants.JOB_RECOMMEND_SCORE;

@Service
@Slf4j
public class JobInfoServiceImpl extends ServiceImpl<JobInfoDao, JobInfo> implements JobInfoService {
    @Resource
    private JobPositionRepository jobPositionRepository;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private UserRepository userRepository;

    @Override
    //TODO 将这个方法配合es搜索引擎一起使用
    public Result getBestJob() {
        Integer userId = UserHolder.getUser().getUserId();
        Set<String> set = null;
        int count = 0;
        while (set == null) {
            set = stringRedisTemplate.opsForZSet().reverseRangeByScore(USER_BEST_JOB + userId, 0, 20);
            count++;
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            if (count >= 5) {
                return Result.fail("服务器繁忙");
            }
        }
        List<Long> collect = set.stream()
                .map(id -> {
                    double v = Double.parseDouble(id);
                    return Math.round(v);
                })
                .toList();

        List<SearchResult> results = jobPositionRepository.findJobAndCompanyByIds(collect);

        return Result.ok(results);

    }

    @Override
    public Result queryAllTags() {

        //读取标签表
        List<String> list = FileUtil.readLines(System.getProperty("user.dir") + "/src/main/resources/static/computer_terms.txt", "utf-8");

        //将标签分组存集合
        List<List<String>> terms = list.stream().map(s -> Arrays.stream(s.split("、")).toList()).toList();

        return Result.ok(terms);
    }

    @Override
    public Result queryHotTags() {
        Set<String> set = stringRedisTemplate.opsForZSet().reverseRangeByScore(HOT_JOB_TAGS, 0, 10);
        return Result.ok(set);
    }

    @Override
    public Result recordScore(JobMark jobMark) {
        Integer jobId = jobMark.getJobId();
        String score = jobMark.getScore();
        UserDTO user = UserHolder.getUser();
        String encode = encode(user.getUserId().toString());
        stringRedisTemplate.opsForZSet().add(JOB_RECOMMEND_SCORE,encode,Double.parseDouble(score));
        stringRedisTemplate.opsForHash().put(JOB_SATISFACTION_SCORE+user.getUserId().toString(),jobId.toString(),score);
        return Result.ok();
    }

    @Override
    public Result getRecordScore() {
        //从缓存获取数据
        Set<ZSetOperations.TypedTuple<String>> tuples = stringRedisTemplate.opsForZSet().rangeWithScores(JOB_RECOMMEND_SCORE, 0, -1);
        //获取全部分数
        List<Double> scores = Optional.ofNullable(tuples)
                .map(tupleSet -> tupleSet.stream()
                        .map(ZSetOperations.TypedTuple::getScore)
                        .collect(Collectors.toList()))
                .orElse(Collections.emptyList());
        // 计算平均值
        double average;
        if (!scores.isEmpty()) {
            average = scores.stream()
                    .mapToDouble(Double::doubleValue)
                    .average()
                    .orElse(0.0);
        } else {
            average = 0.0;
        }
        //返回推荐匹配的评分分数
        return Result.ok(average==0.0?100:average);
    }

    @Override
    public Result getGraph() {
        UserDTO user = UserHolder.getUser();
        Optional<UserNode> userNodeOptional = userRepository.findById(user.getUserId());
        if (userNodeOptional.isPresent()){
            UserNode userNode = userNodeOptional.get();
            log.info("用户 "+user.getUserName()+" 已获取知识图谱");
            //将数据处理为节点和边
            Graph graph = handlerData2Graph(userNode);

            return Result.ok(graph);
        }
        return Result.fail("用户不存在");
    }

    private Graph handlerData2Graph(UserNode userNode) {
        Graph graph = new Graph();
        Set<Node> nodes = new HashSet<>();
        Set<Edge> edges = new HashSet<>();

        Integer userId = userNode.getUserId();
        String userName = userNode.getUserName();
        List<ResumeNode> resumeNodes = userNode.getResumeNodes();

        Node uNode = new Node();
        uNode.setId("User"+userId.toString());
        uNode.setName(userName);
        uNode.setTag("User");
        uNode.setChildrenNum(resumeNodes.size());
        uNode.setIsLeaf(false);
        uNode.setLevel(0);
        uNode.setTags(new ArrayList<>());
        uNode.setInfo(new ArrayList<>());

        nodes.add(uNode);


        for (ResumeNode resumeNode : resumeNodes) {
            Integer rId = resumeNode.getResumeId();
            List<String> rInfo = resumeNode.getTags();
            List<MatchWithRelation> matchWithRelations = resumeNode.getMatchWithRelations();

            Node rNode = new Node();
            rNode.setId("Resume"+rId.toString());
            rNode.setName(resumeNode.getResumeName());
            rNode.setLevel(0);
            rNode.setTag("Resume");
            rNode.setChildrenNum(matchWithRelations.size());
            rNode.setInfo(rInfo);
            rNode.setIsLeaf(false);
            rNode.setTags(new ArrayList<>());
            rNode.setLink("");
            nodes.add(rNode);

            Edge urEdge = new Edge();
            urEdge.setSource(uNode.getId());
            urEdge.setTarget(rNode.getId());
            urEdge.setLabel("");
            urEdge.setId("");
            edges.add(urEdge);

            if(matchWithRelations.size()>50)
                matchWithRelations = matchWithRelations.subList(0,50);
            for (MatchWithRelation matchWithRelation : matchWithRelations) {
                JobPositionNode jobPositionNode = matchWithRelation.getJobPositionNode();
                Double matchScore = matchWithRelation.getMatchScore();

                Node jNode = new Node();
                jNode.setId("Job"+jobPositionNode.getJobPositionId().toString());
                jNode.setName(jobPositionNode.getTitle());
                jNode.setLevel(2);
                jNode.setTag("Job");
                jNode.setChildrenNum(2);
                jNode.setInfo(jobPositionNode.getTag());
                jNode.setLink(jobPositionNode.getLink());
                jNode.setIsLeaf(true);
                jNode.setTags(Collections.singletonList(rNode.getTag()));
                nodes.add(jNode);

                Edge rjEdge = new Edge();
                rjEdge.setSource(rNode.getId());
                rjEdge.setTarget(jNode.getId());
                rjEdge.setLabel(matchScore.toString());
                rjEdge.setId("");
                edges.add(rjEdge);


                Long companyId =  jobPositionNode.getCompany().getCompanyId();
                String companyName = jobPositionNode.getCompany().getCompanyName();

                Node cNode = new Node();
                cNode.setId("Company"+companyId.toString());
                cNode.setName(companyName);
                cNode.setLevel(2);
                cNode.setTag("Company");
                cNode.setChildrenNum(0);
                cNode.setInfo(Collections.singletonList(jobPositionNode.getCompany().getAddress()));
                cNode.setIsLeaf(true);
                cNode.setLink("");
                cNode.setTags(Collections.singletonList(rNode.getTag()));
                nodes.add(cNode);

                Edge jcEdge = new Edge();
                jcEdge.setSource(jNode.getId());
                jcEdge.setTarget(cNode.getId());
                jcEdge.setLabel("");
                jcEdge.setId("");
                edges.add(jcEdge);


                Long educationId =  jobPositionNode.getEducation().getEducationId();
                String educationName = jobPositionNode.getEducation().getEducationName();

                Node eNode = new Node();
                eNode.setId("Education"+educationId.toString());
                eNode.setName(educationName);
                eNode.setLevel(2);
                eNode.setTag("Education");
                eNode.setChildrenNum(0);
                eNode.setInfo(new ArrayList<>());
                eNode.setIsLeaf(true);
                eNode.setLink("");
                eNode.setTags(Collections.singletonList(rNode.getTag()));
                nodes.add(eNode);

                Edge jeEdge = new Edge();
                jeEdge.setSource(jNode.getId());
                jeEdge.setTarget(eNode.getId());
                jeEdge.setLabel("");
                jeEdge.setId("");
                edges.add(jeEdge);
            }
        }

        graph.setNodes(nodes.stream().toList());
        graph.setEdges(edges.stream().toList());
        return graph;
    }

    private String encode(String id){
        // 假设附加一个时间戳作为区分不同编码版本的信息
        long timestamp = System.currentTimeMillis();
        String combined = id + "_" + timestamp;
        byte[] bytes = combined.getBytes(StandardCharsets.UTF_8);
        return cn.hutool.core.codec.Base64.encode(bytes);
    }
    private String decode(String encoded){
        byte[] decodedBytes = Base64.decode(encoded);
        String combinedStr = new String(decodedBytes, StandardCharsets.UTF_8);
        String[] parts = combinedStr.split("_", 2);
        if (parts.length != 2) {
            throw new IllegalArgumentException("Invalid encoded format!");
        }
        // 解析字符串以获取原始ID
        return parts[0];
    }

}
